package memetic.operators;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class NeighborhoodGeneration {
	
	/**
	 * Calculates the factorial for a given number
	 */
	private double factorial(int n){
		double f=1;
		for(int i=1;i<=n;i++){
			f=f*i;
		}
		return f;
	}
	/**
	 * Receives an Integers List and return in a int [] form.
	 */
	private int[] convertArrListInteger_to_Arr(List<Integer> integers){
		int[] ret = new int[integers.size()];
	    Iterator<Integer> iterator = integers.iterator();
	    for (int i = 0; i < ret.length; i++)
	    {
	        ret[i] = iterator.next().intValue();
	    }
	    return ret;
	}
	/**
	 * Return the all possible neighbors from a individual given a Hamming Distance equals to one
	 * @param int [] Individual
	 * @return int [][] neighborhood
	 */
	public int[][] hammingDistance_one(int[] individual){
		int hammingDist_value=1;
		//Calculate the number of bits than can be changed from 0->1
		int avaliableGens=0;
		int i,j;
		List<Integer> posAvalGens=new ArrayList<Integer>();
		for(i=0;i<individual.length;i++){
			if(individual[i]==0){
				posAvalGens.add(new Integer(i));
				avaliableGens++;
			}
		}
		int []avaliableGensArr=new int[avaliableGens];
		avaliableGensArr=convertArrListInteger_to_Arr(posAvalGens);
		//Calculate the all possible number combinations given a Hamming distance 
		int numberofNeighboors=avaliableGens-hammingDist_value;
		int [][] neighborhood=new int[numberofNeighboors][individual.length];
		int posChange=0;
		for(i=0;i<numberofNeighboors;i++){
			//_________________________
			neighborhood[i]=individual.clone();
			posChange=avaliableGensArr[i];
			neighborhood[i][posChange]=1;
		}
		return neighborhood;
	}
	/**
	 * Return the all possible neighbors from a individual given a Hamming Distance equals to two
	 * @param int [] Individual
	 * @return int [][] neighborhood
	 */
	public int[][] hammingDistance_two(int[] individual){
		int hammingDist_value=1;
		//Calculate the number of bits than can be changed from 0->1
		int avaliableGens=0;
		int i,j;
		List<Integer> posAvalGens=new ArrayList<Integer>();
		for(i=0;i<individual.length;i++){
			if(individual[i]==0){
				posAvalGens.add(new Integer(i));
				avaliableGens++;
			}
		}
		int []avaliableGensArr=new int[avaliableGens];
		avaliableGensArr=convertArrListInteger_to_Arr(posAvalGens);
		posAvalGens.clear();
		//Calculate the all possible number combinations given a Hamming distance 
		int numberofNeighboors=(int)(factorial(avaliableGens)/(factorial(hammingDist_value)*factorial(avaliableGens-hammingDist_value)));
		int changeAblePosSet[][]=new int[numberofNeighboors][2];
		int row=0;
		for (i = 0; i < (avaliableGensArr.length-1); i++) {
			changeAblePosSet[i][0]=avaliableGensArr[i];
			for (j = i+1; j < avaliableGensArr.length; j++) {
				changeAblePosSet[row][0]=avaliableGensArr[i];
				changeAblePosSet[row][1]=avaliableGensArr[j];
			}
		}
		//____________________________
		int [][] neighborhood=new int[numberofNeighboors][individual.length];
		int posChange_I=0,posChange_II=0;
		for(i=0;i<numberofNeighboors;i++){
			neighborhood[i]=individual;
			posChange_I=changeAblePosSet[i][0];
			posChange_II=changeAblePosSet[i][1];
			neighborhood[i][posChange_I]=1;
			neighborhood[i][posChange_II]=1;
		}
		return neighborhood;
	}
}
